﻿using System;
using System.Windows;
using System.Windows.Controls;

namespace YourLastOptionsDialog.Controls
{
    /// <summary>
    /// A user control that wraps a password box and adds validation features to it.
    /// </summary>
    public partial class ValidatingPasswordBox : UserControl
    {
        /// <summary>
        /// Identifies the <see cref="ValidatorProperty"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ValidatorProperty = DependencyProperty.Register(
            "Validator", 
            typeof(IValidator), 
            typeof(ValidatingPasswordBox), 
            new PropertyMetadata(default(IValidator), null));

        /// <summary>
        /// Gets or sets the validator used for validation.
        /// </summary>
        public IValidator Validator
        {
            get
            {
                return (IValidator)GetValue(ValidatorProperty);
            }

            set
            {
                SetValue(ValidatorProperty, value);
            }
        }

        /// <summary>
        /// Identifies the <see cref="ValidationErrorMessage"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ValidationErrorMessageProperty = DependencyProperty.Register(
            "ValidationErrorMessage", 
            typeof(string), 
            typeof(ValidatingPasswordBox), 
            new PropertyMetadata("Invalid value.", ValidationErrorMessage_Changed));

        private static void ValidationErrorMessage_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = d as ValidatingPasswordBox;
            if (obj != null)
            {
                var textBlock = obj.ValidationMessageTextBlock;
                if (textBlock != null)
                {
                    textBlock.Text = (string)e.NewValue;
                }
            }
        }

        /// <summary>
        /// Gets or sets the validation error message that is displayed when the validation fails.
        /// </summary>
        public string ValidationErrorMessage
        {
            get
            {
                return (string)GetValue(ValidationErrorMessageProperty);
            }

            set
            {
                SetValue(ValidationErrorMessageProperty, value);
            }
        }

        /// <summary>
        /// Identifies the <see cref="ValidationErrorMessageVisibility"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ValidationErrorMessageVisibilityProperty = DependencyProperty.Register(
            "ValidationErrorMessageVisibility", 
            typeof(Visibility), 
            typeof(ValidatingPasswordBox), 
            new PropertyMetadata(Visibility.Visible, null));

        /// <summary>
        /// Gets or sets the validation error message visibility that determines whether the text box should show 
        /// its own validation error message. If a user of the text box wants to display their own validation error,
        /// this can be set to <see cref="Visibility.Collapsed"/> to suppress the display of the validation message.
        /// </summary>
        public Visibility ValidationErrorMessageVisibility
        {
            get
            {
                return (Visibility)GetValue(ValidationErrorMessageVisibilityProperty);
            }

            set
            {
                SetValue(ValidationErrorMessageVisibilityProperty, value);
            }
        }

        /// <summary>
        /// Identifies the <see cref="IsValid"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsValidProperty = DependencyProperty.Register(
            "IsValid", 
            typeof(bool), 
            typeof(ValidatingPasswordBox), 
            new PropertyMetadata(true, IsValid_Changed));

        private static void IsValid_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = d as ValidatingPasswordBox;
            if (obj != null)
            {
                var newValue = (bool)e.NewValue;
                if (newValue)
                {
                    obj.ValidationMessageTextBlock.Visibility = Visibility.Collapsed;
                }
                else
                {
                    obj.ValidationMessageTextBlock.Visibility = obj.ValidationErrorMessageVisibility;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the value of the text box is valid or not.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the value is valid; otherwise, <c>false</c>.
        /// </value>
        public bool IsValid
        {
            get
            {
                return (bool)GetValue(IsValidProperty);
            }

            set
            {
                SetValue(IsValidProperty, value);
            }
        }

        /// <summary>
        /// Identifies the <see cref="Password"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register(
            "Password", 
            typeof(string), 
            typeof(ValidatingPasswordBox), 
            new PropertyMetadata(string.Empty, Password_Changed));

        private static void Password_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = d as ValidatingPasswordBox;
            if (obj != null)
            {
                var newValue = (string)e.NewValue;
                obj.PasswordBox.Password = newValue ?? string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get
            {
                return (string)GetValue(PasswordProperty);
            }

            set
            {
                SetValue(PasswordProperty, value);
            }
        }

        /// <summary>
        /// Occurs when the password has changed.
        /// </summary>
        public event EventHandler<EventArgs> PasswordChanged;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidatingPasswordBox"/> class.
        /// </summary>
        public ValidatingPasswordBox()
        {
            InitializeComponent();
        }

        private void PasswordBox_OnLostFocus(object sender, RoutedEventArgs e)
        {
            // synchronize back to data
            Password = PasswordBox.Password;

            var handlers = PasswordChanged;
            if (handlers != null)
            {
                PasswordChanged(this, EventArgs.Empty);
            }
        }
    }
}